(** 
Sometimes manual garbage collection is necessary.  This is usually
the case when using external C libraries that allocate small pieces
of the heap.  

For example, to use the MPFI interval arithmetic
library from C, you allocate space for a new interval and then
use this piece of memory as an interval.  For programs that manipulate
lots of intervals, we want to avoid many calls to the malloc system
call.  Thus we would allocate a large number of intervals at once.

From OCaml, if [[interval]] is an external data type, we can
get it to work nicely with the Ocaml garbage collector.  Every
time we need a new interval, we'd allocate a new interval on the
C heap, and the finalization function could deallocate it from
the heap when the pointer is garbage collected in OCaml.  This
leads to too many system calls.  We could have a C wrapper around
MPFI that does manual memory management with, say, an array, but
we prefer to write this code in OCaml.  That is the task of this
module.





*)

(**
   Poolable types must be able to create a new arbitrary element.
   This is the reason we don't use parametric polymorphism here:
   there are no "default" elements of type 'a.
   It should be a function [unit -> t] rather than an element
   of type t because it might call some external C function
   to generate new C heap storage for the element.  
*)
(* module type POOLABLE = *)
(* sig *)
(*   type t *)
(*   val create : unit -> t *)
(* end *)

(**  *)
module type POOLABLE =
sig

  (** comment for t *)
  type t
  val create : unit -> t
  val init : int -> (int -> t)

end

module ArrayPoolable
  (T : sig type t val create : unit -> t end) : POOLABLE 
  with type t = T.t

module type T = 
sig
  
  module P : POOLABLE

  type t
  
  (** project from the POOLABLE structure to the base type*)
  val proj : t -> P.t

  (** init size keep
      initializes the list, containing at most 'size' elements,
      and assuming you keep about 'keep' elements at any given time *)
  val init : size:int -> keep:int -> unit

  (** Number of live elements currently in the pool. *)
  val used : unit -> int

  (** Capacity of the pool. *)
  val size : unit -> int

  (** new_() returns a new memory location if one is available.  
      This is not the same as malloc, in the sense that if 
      do_gc() is called, this memory might be reclaimed.  Thus,
      do_gc must only be called when one is sure all necessary 
      values have been saved.  *)
  val new_ : unit -> t

  (** Mark an item as live, thus garbage collection will leave
      it alone. *)
  val save : t -> unit
  (** Indicate you are finished with the item.  The next garbage collection
      will collect it. *)
  val delete : t -> unit

  (** Run the garbage collector. *)
  val do_gc : unit -> unit

  module Ops : 
  sig
    (** Nice syntax for projection: !!x := proj x *)
    val (!!) : t -> P.t
  end 

end 

(** A concrete ArrayPoolable structure. *)
module S(P : POOLABLE) : T with module P = P
